package com.xiaoxin.experience.service.http;

import com.xiaoxin.experience.api.hls.request.CreateHlsTaskRequest;
import com.xiaoxin.experience.api.hls.request.HlsTaskListResponse;
import com.xiaoxin.experience.api.hls.request.RetryHlsTaskRequest;
import com.xiaoxin.experience.api.http.model.AuthInfo;
import com.xiaoxin.experience.api.http.model.RequestInfo;
import com.xiaoxin.experience.api.http.model.RequestMessage;
import com.xiaoxin.experience.api.http.model.ResponseInfo;
import com.xiaoxin.experience.api.http.request.*;
import com.xiaoxin.experience.common.BaseResponse;
import com.xiaoxin.experience.common.ErrorCode;
import com.xiaoxin.experience.common.Property;
import com.xiaoxin.experience.common.RestResponse;
import com.xiaoxin.experience.service.hls.HlsService;
import com.xiaoxin.experience.service.hls.common.HlsTask;
import com.xiaoxin.experience.service.http.storage.EnvironmentFileHandler;
import com.xiaoxin.experience.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.FileSystemResource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author xiaoxin
 */
@Slf4j
@Service
public class HttpService
{
    @Resource
    RestTemplate restTemplate;

    @Resource
    HlsService hlsService;

    public String uploadFile(UploadFileRequest request)
    {
        File file = new File(request.getFilePath());

        //设置请求头
        HttpHeaders headers = new HttpHeaders();
        //1.设置请求类型 上传文件必须用表单类型
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        //2.设置内容长度，必须设置
        headers.setContentLength(file.length());
        //3.设置文件名称，处理文件名是中文的情况
        headers.setContentDispositionFormData("fileName", URLEncoder.encode(file.getName()));

        //4.设置请求体，注意是LinkedMultiValueMap
        MultiValueMap<String, Object> form = new LinkedMultiValueMap<>();

        // 其他参数
        if (CollectionUtil.isNotEmpty(request.getOtherProperties()))
        {
            request.getOtherProperties().forEach(property ->
            {
                form.add(property.getKey(), property.getValue());
            });
        }
        //5.把文件填充到表单里，注意使用FileSystemResource
        form.add("file", new FileSystemResource(file));
        HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(form, headers);
        //6.发起请求
        ResponseEntity<String> responseEntity = restTemplate.postForEntity(request.getUploadUrl(), requestEntity, String.class);
        return responseEntity.getBody();
    }


    private List<Property> getHeader(HttpHeaders headers)
    {
        List<Property> header = new ArrayList<>();
        if (Objects.isNull(headers))
        {
            return header;
        }
        Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
        for (Map.Entry<String, List<String>> entry : entries)
        {
            String value = entry.getValue().toString().replace("[", "").replace("]", "");
            header.add(new Property(entry.getKey(), value));
        }
        return header;
    }

    public CronCheckResponse cronCheck(CronCheckRequest request)
    {
        String cron = request.getCron();
        int times = request.getTimes();
        CronCheckResponse response = new CronCheckResponse();
        if (StringUtil.isBlank(cron))
        {
            response.setResultCode(ErrorCode.ERROR_PARAMETER);
            response.setResultDesc("cron is empty");
            return response;
        }
        if (CronUtil.valid(cron))
        {
            List<String> runTimes = CronUtil.next(cron, times == 0 ? 5 : times, t -> DateUtil.formatTime(t, DateUtil.YMDHMS_STD));
            StringBuilder sb = new StringBuilder();
            runTimes.forEach(runTime -> sb.append(runTime).append("\n"));
            response.setRunTimes(sb.toString());
            return response;
        }
        response.setResultCode(ErrorCode.ERROR_PARAMETER);
        response.setResultDesc("cron is valid");
        return response;
    }

    public BaseResponse createHlsTask(CreateHlsTaskRequest request)
    {
        String downloadUrl = request.getDownloadUrl();
        String name = request.getName();
        String savePath = request.getSavePath();
        HlsTask hlsTask = new HlsTask(name,savePath,downloadUrl);
        hlsService.download(hlsTask);
        return new BaseResponse();
    }

    public BaseResponse retryHlsTask(RetryHlsTaskRequest request)
    {
        hlsService.retry(request.getTaskId());
        return new BaseResponse();
    }

    public BaseResponse removeHlsTask(RemoveHlsTaskRequest request)
    {
        hlsService.remove(request.getTaskId(), request.isRemoveFile());
        return new BaseResponse();
    }

    public HlsTaskListResponse hlsTasks()
    {
        HlsTaskListResponse hlsTaskListResponse = new HlsTaskListResponse();
        hlsTaskListResponse.setHlsTasks(hlsService.getAllHlsTasks());
        return hlsTaskListResponse;
    }


    private HttpHeaders getJsonHttpHeaders()
    {
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        return httpHeaders;
    }

    private String getDigestMessage(ResponseEntity<String> response)
    {
        HttpHeaders headers = response.getHeaders();
        String digestMessage = "";
        Set<Map.Entry<String, List<String>>> entries = headers.entrySet();
        for (Map.Entry<String, List<String>> entry : entries)
        {
            if ("WWW-Authenticate".equals(entry.getKey()))
            {
                digestMessage = entry.getValue().get(0);
                break;
            }
        }
        return digestMessage;
    }

    public RestResponse<ResponseInfo> request(RequestInfo request)
    {
        ResponseInfo response = new ResponseInfo();
        // 获取请求地址
        String requestUrl = parseRequestUrl(request.getRequestUrl(),request.getRequestPathParam(),request.getRequestParam());
        String requestMethod = request.getRequestMethod();
        HttpMethod httpMethod = HttpMethod.resolve(requestMethod);
        if (Objects.isNull(httpMethod))
        {
            httpMethod = HttpMethod.GET;
        }
        HttpHeaders httpHeaders = getHttpHeaders(request.getRequestHeader(),request.getRequestType());
        ResponseEntity<String> responseEntity = getResponseEntity(requestUrl,httpMethod,httpHeaders,request);
        response.setResponseCode(responseEntity.getStatusCodeValue());
        response.setResponseHeader(getHeader(responseEntity.getHeaders()));
        response.setResponseBody(responseEntity.getBody());
        return RestResponse.success(response);
    }

    private HttpHeaders getHttpHeaders(List<Property> requestHeader,String requestType)
    {
        HttpHeaders httpHeaders = new HttpHeaders();
        if (StringUtil.isBlank(requestType))
        {
            requestType = "0";
        }
        switch (requestType)
        {
            case "1":
                httpHeaders.setContentType(MediaType.APPLICATION_ATOM_XML);
                break;
            case "2":
                httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
                break;
            default:
                httpHeaders.setContentType(MediaType.APPLICATION_JSON);
                break;
        }
        httpHeaders.setAccept(Collections.singletonList(MediaType.ALL));
        httpHeaders.setAcceptCharset(Collections.singletonList(StandardCharsets.UTF_8));
        if (CollectionUtil.isNotEmpty(requestHeader))
        {
            for (Property property : requestHeader)
            {
                if (property.isValid())
                {
                    httpHeaders.add(property.getKey(),property.getValue());
                }
            }
        }
        return httpHeaders;
    }

    private <T> HttpEntity<T> getHttpEntity(T body,HttpHeaders headers)
    {
        return new HttpEntity<>(body,headers);
    }

    private ResponseEntity<String> getResponseEntity(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,RequestInfo request)
    {
        AuthInfo auth = request.getAuth();
        if (auth == null)
        {
            auth = new AuthInfo();
        }
        ResponseEntity<String> responseEntity;
        try
        {
            switch (auth.getType())
            {
                case "Basic":
                    responseEntity = basicAuthRequest(requestUrl,httpMethod,httpHeaders,request);
                    break;
                case "Digest":
                    responseEntity = digestAuth(requestUrl,httpMethod,httpHeaders,request);
                    break;
                default:
                    responseEntity = noneAuthRequest(requestUrl,httpMethod,httpHeaders,request);
                    break;
            }
        }
        catch (Exception e)
        {
            log.error("request error",e);
            return new ResponseEntity<>(e.getMessage(),HttpStatus.INTERNAL_SERVER_ERROR);
        }
        return responseEntity;
    }

    private ResponseEntity<String> noneAuthRequest(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,RequestInfo request)
    {
        if (isUrlFormEncoded(request.getRequestType()))
        {
            return urlFormEncodedRequest(requestUrl,httpMethod,httpHeaders,request.getRequestForm());
        }
        return restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(request.getRequestBody(),httpHeaders), String.class);
    }

    private ResponseEntity<String> basicAuthRequest(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,RequestInfo request)
    {
        String basic = request.getAuth().getUsername() + ":" + request.getAuth().getPassword();
        String basicAuth = Base64.getEncoder().encodeToString(basic.getBytes());
        String authorization = "Basic " + basicAuth;
        httpHeaders.set("Authorization",authorization);
        if (isUrlFormEncoded(request.getRequestType()))
        {
            return urlFormEncodedRequest(requestUrl,httpMethod,httpHeaders,request.getRequestForm());
        }
        return restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(request.getRequestBody(),httpHeaders), String.class);
    }

    private ResponseEntity<String> digestAuth(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,RequestInfo request)
    {
        ResponseEntity<String> response = isUrlFormEncoded(request.getRequestType()) ? urlFormEncodedRequest(requestUrl,httpMethod,httpHeaders,request.getRequestForm())
                : restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(request.getRequestBody(),httpHeaders), String.class);
        int statusCodeValue = response.getStatusCodeValue();
        if (DigestUtil.UNAUTHORIZED == statusCodeValue)
        {
            String digestMessage = getDigestMessage(response);
            if (StringUtil.isBlank(digestMessage))
            {
                return response;
            }
            String authorization = DigestUtil.getAuthorization(digestMessage, request.getAuth().getUsername(), request.getAuth().getPassword(), httpMethod.name(), requestUrl, null);
            setAuthorization(httpHeaders,authorization);
            response = isUrlFormEncoded(request.getRequestType()) ? urlFormEncodedRequest(requestUrl,httpMethod,httpHeaders,request.getRequestForm())
                    : restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(request.getRequestBody(),httpHeaders), String.class);
        }
        return response;
    }

    private boolean isUrlFormEncoded(String requestType)
    {
        return "2".equals(requestType);
    }

    private ResponseEntity<String> urlFormEncodedRequest(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,List<Property> requestForm)
    {
        MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<>();
        if (CollectionUtil.isNotEmpty(requestForm))
        {
            for (Property property : requestForm)
            {
                requestBody.add(property.getKey(),property.getValue());
            }
        }
        return restTemplate.exchange(requestUrl,httpMethod ,getHttpEntity(requestBody,httpHeaders), String.class);
    }

    public RedirectResponse request(RequestMessage requestMessage)
    {
        RedirectResponse redirectResponse = new RedirectResponse();

        // 获取请求地址
        String requestUrl = parseRequestUrl(requestMessage.getRequestUrl(),requestMessage.getRequestPathParam(),requestMessage.getRequestParam());
        String requestMethod = requestMessage.getRequestMethod();
        HttpMethod httpMethod = HttpMethod.resolve(requestMethod);
        if (Objects.isNull(httpMethod))
        {
            httpMethod = HttpMethod.GET;
        }
        HttpHeaders httpHeaders = buildHeader(requestMessage.getRequestHeader());
        long beginSendRequestTime = System.currentTimeMillis();
        ResponseEntity<String> responseEntity;
        switch (requestMessage.getRequestAuth())
        {
            case "Basic":
                responseEntity = basicAuthRequest(requestUrl,httpMethod,httpHeaders,requestMessage.getRequestBody(),requestMessage.getUsername(),requestMessage.getPassword());
                break;
            case "Digest":
                responseEntity = digestAuth(requestUrl,httpMethod,httpHeaders,requestMessage.getRequestBody(),requestMessage.getUsername(),requestMessage.getPassword());
                break;
            case "UrlEncoded":
                responseEntity = formUrlEncodedRequest(requestUrl, requestMessage.getRequestHeader());
                break;
            default:
                responseEntity = noneAuthRequest(requestUrl, httpMethod, httpHeaders, requestMessage.getRequestBody());
                break;
        }
        redirectResponse.setResponseTime((System.currentTimeMillis() - beginSendRequestTime) + "ms");
        redirectResponse.setResultCode(responseEntity.getStatusCodeValue());
        redirectResponse.setHeader(getHeader(responseEntity.getHeaders()));
        redirectResponse.setBody(responseEntity.getBody());
        return redirectResponse;
    }

    private ResponseEntity<String> formUrlEncodedRequest(String requestUrl,List<Property> requestParam)
    {
        //注意要使用MultiValueMap
        MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>();
        if (CollectionUtil.isNotEmpty(requestParam))
        {
            for (Property property : requestParam)
            {
                if (property.isValid())
                {
                    paramsMap.add(property.getKey(), property.getValue());
                }
            }
        }

        //设置请求信息
        RequestEntity<MultiValueMap<String, String>> requestEntity = RequestEntity
                .post("")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .accept(MediaType.ALL)
                .acceptCharset(StandardCharsets.UTF_8)
                .body(paramsMap);

        return restTemplate.postForEntity(requestUrl, requestEntity, String.class);
    }

    private ResponseEntity<String> noneAuthRequest(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,String requestBody)
    {
        return restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(requestBody,httpHeaders), String.class);
    }

    private ResponseEntity<String> basicAuthRequest(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,String requestBody,String username,String password)
    {
        String basic = username + ":" + password;
        String basicAuth = Base64.getEncoder().encodeToString(basic.getBytes());
        String authorization = "Basic " + basicAuth;
        setAuthorization(httpHeaders,authorization);
        return restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(requestBody,httpHeaders), String.class);
    }

    private ResponseEntity<String> digestAuth(String requestUrl,HttpMethod httpMethod,HttpHeaders httpHeaders,String requestBody,String username,String password)
    {
        ResponseEntity<String> response = restTemplate.exchange(requestUrl, httpMethod, new HttpEntity<>(requestBody,httpHeaders), String.class);
        int statusCodeValue = response.getStatusCodeValue();
        if (DigestUtil.UNAUTHORIZED == statusCodeValue)
        {
            String digestMessage = getDigestMessage(response);
            if (StringUtil.isBlank(digestMessage))
            {
                return response;
            }
            String authorization = DigestUtil.getAuthorization(digestMessage, username, password, httpMethod.name(), requestUrl, null);
            setAuthorization(httpHeaders,authorization);
            response = restTemplate.exchange(requestUrl,httpMethod,new HttpEntity<>(requestBody,httpHeaders),String.class);
        }
        return response;
    }

    private String parseRequestUrl(String requestUrl,List<Property> requestPathParam,List<Property> requestParam)
    {
        if (CollectionUtil.isEmpty(requestPathParam) && CollectionUtil.isEmpty(requestParam))
        {
            return requestUrl;
        }
        StringBuilder parsedUrl = new StringBuilder(requestUrl);
        for (Property property : requestPathParam)
        {
            if (property.isValid())
            {
                parsedUrl = new StringBuilder(parsedUrl.toString().replace(property.getKey(), property.getValue()));
            }
        }
        if (CollectionUtil.isNotEmpty(requestParam))
        {
            boolean isFirst = !requestUrl.contains("?");
            for (Property property : requestParam)
            {
                if (property.isValid())
                {
                    parsedUrl.append(isFirst ? "?" : "&");
                    parsedUrl.append(property.getKey()).append("=").append(urlEncode(property.getValue()));
                    isFirst = false;
                }
            }
        }
        return parsedUrl.toString();
    }

    private String urlEncode(String url)
    {
        try
        {
            return URLEncoder.encode( url, "UTF-8" );
        }
        catch (Exception e)
        {
            return url;
        }

    }

    private HttpHeaders buildHeader(List<Property> requestHeader)
    {
        HttpHeaders httpHeaders = getJsonHttpHeaders();
        if(CollectionUtil.isNotEmpty(requestHeader))
        {
            for (Property property : requestHeader)
            {
                if (property.isValid())
                {
                    httpHeaders.add(property.getKey(),property.getValue());
                }
            }
        }
        return httpHeaders;
    }

    private void setAuthorization(HttpHeaders httpHeaders,String authorization)
    {
        httpHeaders.set("Authorization",authorization);
    }

    public List<String> getEnvironmentNames()
    {
        return EnvironmentFileHandler.getEnvironmentNames();
    }

    public List<Property> getEnvironment(String environmentName)
    {
        List<Property> properties = new ArrayList<>();
        Map<String, String> environmentMessage = EnvironmentFileHandler.getEnvironmentMessage(environmentName);
        if (CollectionUtil.isEmpty(environmentMessage))
        {
            return properties;
        }

        environmentMessage.forEach((k,v)->properties.add(new Property(k,v)));
        return properties;
    }

    public void createEnvironment(String environmentName,List<Property> properties)
    {
        EnvironmentFileHandler.createNewEnvironment(environmentName,properties);
    }

    public void delEnvironment(String environmentName)
    {
        EnvironmentFileHandler.deleteEnvironment(environmentName);
    }

    public void addEnvironmentProperty(String environment,Property property)
    {
        if (property.isValid())
        {
            EnvironmentFileHandler.addProperty(environment,property.getKey(),property.getValue());
        }
    }

    public void delEnvironmentProperty(String environmentName,String key)
    {
        EnvironmentFileHandler.delProperty(environmentName,key);
    }

}
